from minio import Minio
from minio.error import S3Error
from app.core.config import settings
import logging
from datetime import timedelta
from typing import Union
import boto3
from botocore.client import Config

# 配置日志记录器
logger = logging.getLogger(__name__)
logger.info("MinIO 客户端模块加载")

class MinioClient:
    _instance = None
    _initialized = False

    def __new__(cls):
        if cls._instance is None:
            logger.info("创建 MinIO 客户端实例")
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self):
        if not MinioClient._initialized:
            logger.info("==== 开始初始化 MinIO 客户端 ====")
            logger.info(f"配置文件中的 MinIO 端点: {settings.MINIO_ENDPOINT}")
            
            # 检查 endpoint 格式
            endpoint = settings.MINIO_ENDPOINT
            if endpoint.startswith('http://'):
                endpoint = endpoint[7:]
                logger.info("移除 http:// 前缀")
            elif endpoint.startswith('https://'):
                endpoint = endpoint[8:]
                logger.info("移除 https:// 前缀")
            
            # 保存原始和处理后的端点地址
            self.original_endpoint = settings.MINIO_ENDPOINT
            self.endpoint = endpoint
            logger.info(f"处理后的 MinIO 端点: {self.endpoint}")
            
            # 根据配置确定是否使用 HTTPS
            secure = settings.MINIO_SECURE
            logger.info(f"MinIO 连接使用 {'HTTPS' if secure else 'HTTP'}")
            
            logger.info("正在创建 MinIO 客户端连接...")
            self.client = Minio(
                endpoint,
                access_key=settings.MINIO_ACCESS_KEY,
                secret_key=settings.MINIO_SECRET_KEY,
                secure=secure
            )
            self.bucket_name = settings.MINIO_BUCKET_NAME
            logger.info(f"MinIO 客户端连接创建成功，bucket: {self.bucket_name}")
            
            # 初始化 boto3 客户端用于分片上传
            logger.info("正在创建 boto3 S3 客户端...")
            endpoint_url = f"{'https' if secure else 'http'}://{endpoint}"
            self.s3_client = boto3.client(
                's3',
                endpoint_url=endpoint_url,
                aws_access_key_id=settings.MINIO_ACCESS_KEY,
                aws_secret_access_key=settings.MINIO_SECRET_KEY,
                config=Config(signature_version='s3v4'),
                verify=False,  # 对于自签名证书的本地 MinIO
                region_name='us-east-1'  # MinIO 默认区域
            )
            logger.info(f"boto3 S3 客户端创建成功，endpoint: {endpoint_url}")
            
            self._ensure_bucket()
            logger.info("==== MinIO 客户端初始化完成 ====")
            MinioClient._initialized = True

    def _ensure_bucket(self):
        """确保存储桶存在"""
        try:
            if not self.client.bucket_exists(self.bucket_name):
                logger.debug(f"存储桶 {self.bucket_name} 不存在，正在创建...")
                self.client.make_bucket(self.bucket_name)
                logger.debug(f"存储桶 {self.bucket_name} 创建成功")
            else:
                logger.debug(f"存储桶 {self.bucket_name} 已存在")
        except S3Error as exc:
            logger.error(f"MinIO Error: {exc}")
            raise

    # def upload_file(self, file_object, filename, content_type):
    #     """上传文件到MinIO"""
    #     try:
    #         return self.client.put_object(
    #             bucket_name=settings.minio_bucket_name,
    #             object_name=f"uploads/{filename}",
    #             data=file_object,
    #             content_type=content_type
    #         )
    #     except S3Error as exc:
    #         print(f"Upload failed: {exc}")
    #         raise
    
    def upload_file(self, file_object, object_path, content_type):
        """上传文件到MinIO
        
        Args:
            file_object: 文件对象
            object_path: 对象路径，例如 "user_id/photo/2023/01/01/file.jpg"
            content_type: 文件MIME类型
        """
        try:
            logger.debug(f"正在上传文件到 {self.bucket_name}/{object_path}")
            
            # 获取文件长度
            # 首先保存当前位置
            current_position = file_object.tell()
            # 移动到文件末尾
            file_object.seek(0, 2)
            # 获取文件大小
            file_size = file_object.tell()
            # 恢复到原始位置
            file_object.seek(current_position)
            
            logger.debug(f"文件大小: {file_size} 字节")
            
            result = self.client.put_object(
                bucket_name=self.bucket_name,
                object_name=object_path,
                data=file_object,
                length=file_size,  # 添加长度参数
                content_type=content_type
            )
            logger.debug(f"文件上传成功: {result.etag}")
            return result
        except S3Error as exc:
            logger.error(f"上传失败: {exc}")
            raise

    def remove_object(self, object_path):
        """从MinIO中删除文件
        
        Args:
            object_path: 对象路径，例如 "user_id/photo/2023/01/01/file.jpg"
        """
        try:
            logger.debug(f"正在从 {self.bucket_name} 删除文件: {object_path}")
            self.client.remove_object(self.bucket_name, object_path)
            logger.debug(f"文件删除成功: {object_path}")
        except S3Error as exc:
            logger.error(f"删除失败: {exc}")
            raise

    def get_presigned_url(self, object_path: str, expires: Union[int, timedelta] = 3600, response_content_type: str = None):
        """生成预签名URL
        
        Args:
            object_path: 对象路径
            expires: URL有效期，可以是秒数(int)或timedelta对象，默认3600秒
            response_content_type: 指定响应的Content-Type，用于预览时设置正确的MIME类型
        
        Returns:
            str: 预签名URL
        """
        try:
            logger.debug(f"正在生成预签名URL: {object_path}")
            logger.debug(f"MinIO 端点: {self.original_endpoint}")
            logger.debug(f"MinIO bucket: {self.bucket_name}")
            
            # 确保 expires 是 timedelta 类型且在有效范围内（最大7天）
            if isinstance(expires, (int, float)):
                seconds = int(expires)
                if seconds > 604800:  # 7天的秒数
                    seconds = 604800
                expires = timedelta(seconds=seconds)
            elif not isinstance(expires, timedelta):
                expires = timedelta(seconds=3600)
            
            # 检查对象是否存在
            try:
                self.client.stat_object(self.bucket_name, object_path)
            except S3Error as e:
                if e.code == 'NoSuchKey':
                    raise FileNotFoundError(f"文件不存在: {object_path}")
                raise
            
            url = self.client.presigned_get_object(
                bucket_name=self.bucket_name,
                object_name=object_path,
                expires=expires,
                response_headers={"response-content-type": response_content_type} if response_content_type else None
            )
            
            # 记录生成的 URL 信息
            logger.debug(f"生成的 URL (部分): {url[:50]}...")
            
            return url
            
        except FileNotFoundError:
            logger.error(f"文件不存在: {object_path}")
            raise
        except S3Error as e:
            logger.error(f"生成预签名URL失败 (S3Error): {e.code} - {e.message}")
            raise
        except Exception as e:
            logger.error(f"生成预签名URL失败: {str(e)}")
            raise

# 创建全局实例
logger.info("正在创建 MinIO 客户端全局实例...")
minio_client = MinioClient()
logger.info("MinIO 客户端全局实例创建完成")

def get_minio_client() -> MinioClient:
    """获取 MinIO 客户端实例（单例模式）"""
    return minio_client